
// =====================================================================================
// =====================================================================================
// Module Name: 	PLC模块的控制
// File Name:		PLC.h
// =====================================================================================
// Version: 		V1.00
// Date:
// Comment:		
//
// =====================================================================================
#ifndef _DL645_H
#define _DL645_H

#include "usart_645.h"
#include "mytype.h"
#include "Comm.h"

// 协议常量定义
#define FRAME_START       0x68
#define FRAME_END         0x16
#define MAX_FRAME_LEN     256
#define ADDR_LEN          6


#ifndef Dl645_PASSWORD_Level
#define Dl645_PASSWORD_Level			10
#endif


// DLT645 Frame EOF/BOF Flag
#define FRAME_BOF						0x68
#define FRAME_EOF						0x16
#define FRAME_HEAD_SIZE					sizeof(struct TagDlt645FrameHead)
#define FRAME_MIN_SIZE					(FRAME_HEAD_SIZE + 2)
#define FRAME_LENGTH(p)					(((Dlt645_Frame_Type *)p)->head.len + FRAME_MIN_SIZE)
#define FRAME_SUM_LENGTH(p)				(((Dlt645_Frame_Type *)p)->head.len + FRAME_HEAD_SIZE)
#define FRAME_SUM_INDEX(p)				(((Dlt645_Frame_Type *)p)->head.len)
#define FRAME_EOF_INDEX(p)				(((Dlt645_Frame_Type *)p)->head.len + 1)

#define FRAME_ADDRLENGTH				6
// DLT645 Address types
#define FRAME_IS_SELF_ADR				0	// 指部分带有通配字符的地址
#define FRAME_IS_BROADCAST_ADR			1
#define FRAME_IS_SUPER_ADR				2
#define FRAME_IS_FULL_ADR				3
#define FRAME_IS_ERR_ADR				0xFF

// DLT645_Proc return code
#define DLT645PROC_RTN_INVALID			0x00
#define DLT645PROC_RTN_ERR				0x01
#define DLT645PROC_RTN_TXING			0x02
#define DLT645PROC_RTN_NOResponse		0x03
// DLT645_Proc return FE nums
#define DLT645PROC_RTN_FENUMS			0x00

#define Dl645_BroadTime 				0x08
#define Dl645_ReadData_2007 			0x11
#define Dl645_ReadSequentData_2007 		0x12
#define Dl645_ReadAddress_2007 			0x13
#define Dl645_WriteData_2007			0x14
#define Dl645_SetAddress_2007 			0x15
#define Dl645_Freeze_2007 				0x16
#define Dl645_ChangeBaudrate_2007 		0x17
#define Dl645_ChangePassword_2007 		0x18
#define Dl645_DemandClear_2007 			0x19
#define Dl645_MeterClear_2007 			0x1A
#define Dl645_EventClear_2007 			0x1B

#define Dl645_WriteData_Subsequent		0x34    //写后续帧


//-----------------------------------------------------------------------------
// 电能量数据标识
#define DI_ACTIVE_ENERGY_TOTAL     0x00000000  // (当前)组合有功总电能
#define DI_ACTIVE_ENERGY_FORWARD   0x00010000  // (当前)正向有功总电能
#define DI_ACTIVE_ENERGY_REVERSE   0x00020000  // (当前)反向有功总电能

// 最大需量及发生时间数据标识
#define DI_MAX_DEMAND_FORWARD_ACTIVE      0x01010000  // (当前)正向有功总最大需量及发生时间
#define DI_MAX_DEMAND_REVERSE_ACTIVE      0x01020000  // (当前)反向有功总最大需量及发生时间
#define DI_MAX_DEMAND_COMBINED_REACTIVE_1 0x01030000 // (当前)组合无功 1 总最大需量及发生时间

// 变量数据标识
#define DI_VOLTAGE_PHASE_A         0x02010000  // A相电压
#define DI_CURRENT_PHASE_A         0x02020000  // A相电流
#define DI_TOTAL_ACTIVE_POWER      0x02030000  // 瞬时总有功功率
#define DI_TOTAL_REACTIVE_POWER    0x02040000  // 瞬时总无功功率
#define DI_TOTAL_APPARENT_POWER    0x02050000  // 瞬时总视在功率
#define DI_TOTAL_POWER_FACTOR      0x02060000  // 总功率因数

// 参变量数据标识
#define DI_DATE_WEEK                    0x04000101  // 日期及星期
#define DI_TIME                         0x04000102  // 时间
#define DI_ACTIVE_REPORT_MODE_WORD      0x04001104  // 主动上报模式字
#define DI_ACTIVE_REPORT_STATUS_WORD    0x04001501  // 主动上报状态字
#define DI_RESET_REPORT_STATE_WORD      0x04001503  // 复位主动上报状态字

#define PASSWORD_LEVEL_03       0x03
#define PASSWORD_LEVEL_04       0x04
#define PASSWORD_LEVEL_96       0x96
#define PASSWORD_LEVEL_97       0x97

// 错误码
#define ERR_RATE_OVERFLOW        0x40  // 费率数超 (Bit6置1)：费率数量超过设备支持最大值
#define ERR_DAY_SEGMENT_OVERFLOW 0x20  // 日时段数超 (Bit5置1)：日时段划分数量超过设备容量
#define ERR_TZ_OVERFLOW          0x10  // 年时区数超 (Bit4置1)：年时区切换点数量超过限制
#define ERR_BAUD_CANT_CHANGE     0x08  // 通信速率不能更改 (Bit3置1)：设备禁止修改通信波特率
#define ERR_PASSWORD             0x04  // 密码错/未授权 (Bit2置1)：密码错误或操作权限不足
#define ERR_NO_DATA              0x02  // 无请求数据 (Bit1置1)：设备不存在请求的数据项
#define ERR_OTHER                0x01  // 其他错误 (Bit0置1)：未明确分类的协议层错误

typedef enum {
    CTRL_CODE_ACTIVE_REPORT_REP       = 0x06, // 主站应答：主动上报响应
    CTRL_CODE_BROADCAST_SYNC_REQ      = 0x08, // 主站请求：广播校时
    CTRL_CODE_READ_REQ                = 0x11, // 主站请求：读数据
    CTRL_CODE_READ_SEQUEL_REQ         = 0x12, // 主站请求：读后续数据
    CTRL_CODE_REPORT_ADDRESS_REQ      = 0x13, // 主站请求：报告通信地址
    CTRL_CODE_WRITE_REQ               = 0x14, // 主站请求：写数据
    CTRL_CODE_WRITE_ADDR_REQ          = 0x15, // 主站请求：写通信地址
    CTRL_CODE_ACTIVE_REPORT_REQ       = 0x86, // 从站请求：主动上报
    CTRL_CODE_READ_OK_REP             = 0x91, // 从站应答：读数据正常响应，无后续数据
    CTRL_CODE_READ_SEQUEL_OK_REP      = 0x92, // 从站应答：读后续数据正常响应，无后续数据
    CTRL_CODE_READ_ADDR_REP           = 0x93, // 从站应答：读通信地址成功响应
    CTRL_CODE_WRITE_OK_REP            = 0x94, // 从站应答：写数据成功响应
    CTRL_CODE_READ_OK_MORE_REP        = 0xB1, // 从站应答：读数据正常响应，有后续数据
    CTRL_CODE_READ_SEQUEL_OK_MORE_REP = 0xB2, // 从站应答：读后续数据正常响应，有后续数据
    CTRL_CODE_READ_NOK_REP            = 0xD1, // 从站应答：读数据异常响应
    CTRL_CODE_READ_SEQUEL_NOK_REP     = 0xD2, // 从站应答：读后续数据异常响应
    CTRL_CODE_WRITE_NOK_REP           = 0xD4, // 从站应答：写数据异常响应
} DL645ControlCode;


// Dlt645 Command Frame format
typedef struct
{
	struct TagDlt645FrameHead
	{
		u8 bof;
		u8 adr[FRAME_ADDRLENGTH];
		u8 sbof;
		u8 ctl;
		u8 len;
	}head;
	u8 data[UART1_HARD_BUF_SIZE - FRAME_HEAD_SIZE];
}Dlt645_Frame_Type;

// 电表地址类型（6字节BCD码）
typedef struct {
    uint8_t addr[ADDR_LEN]; // addr[0]为高位，addr[5]为低位
} MeterAddress;

// 645帧结构体
typedef struct {
    MeterAddress meter_addr;  // 电表地址
    uint8_t control_code;     // 控制码
    uint16_t data_len;        // 数据域长度
    uint8_t data[MAX_FRAME_LEN - 12]; // 数据域(最大空间)
} DL645Frame;

/**
 * @brief 发送645帧
 * @param frame 要发送的帧结构体
 * @param enable_data_transform 是否对数据域进行33H变换
 */
int send_645_frame(const DL645Frame *frame, bool enable_data_transform);

/**
 * @brief 应答645帧
 * @param frame 要发送的帧结构体
 * @param enable_data_transform 是否对数据域进行33H变换
 */
int respone_645_frame(const DL645Frame *frame, bool enable_data_transform);
/**
 * @brief 接收并解析645帧
 * @param data 接收到的原始数据
 * @param len 数据长度
 * @param frame 解析后的帧结构体
 * @return 0-成功, -1-校验失败, -2-格式错误
 */
int receive_645_frame(DL645Frame *frame);


// =====================================================================================
// Macro & Types Definitions
// =====================================================================================

#define PLC_BR_DEFAULT                  (9600)

#define PLC_KEEPLIVE_SESC_DEFAULT       (360)

// PLC引脚控制
#define GPIO_PLC_SET_PORT               (GPIOD)
#define GPIO_PLC_SET_PIN                (GPIO_PIN_15)
#define GPIO_PLC_SET_OFF                ((GPIO_PLC_SET_PORT->clr) = GPIO_PLC_SET_PIN)
#define GPIO_PLC_SET_ON                 ((GPIO_PLC_SET_PORT->scr) = GPIO_PLC_SET_PIN)

#define GPIO_PLC_RST_PORT               (GPIOD)
#define GPIO_PLC_RST_PIN                (GPIO_PIN_0)
#define GPIO_PLC_RST_ON                 ((GPIO_PLC_RST_PORT->clr) = GPIO_PLC_RST_PIN)
#define GPIO_PLC_RST_OFF                ((GPIO_PLC_RST_PORT->scr) = GPIO_PLC_RST_PIN)

#define GPIO_PLC_STA_PORT               (GPIOC)
#define GPIO_PLC_STA_PIN                (GPIO_PIN_12)
#define GPIO_PLC_STA_GET                (GPIO_PLC_STA_PORT->idt & GPIO_PLC_STA_PIN)

// evt拉高为有效
#define GPIO_PLC_EVENTOUT_PORT          (GPIOD)
#define GPIO_PLC_EVENTOUT_PIN           (GPIO_PIN_1)
#define GPIO_PLC_EVENTOUT_OFF           ((GPIO_PLC_EVENTOUT_PORT->clr) = GPIO_PLC_EVENTOUT_PIN)
#define GPIO_PLC_EVENTOUT_ON            ((GPIO_PLC_EVENTOUT_PORT->scr) = GPIO_PLC_EVENTOUT_PIN)
#define GPIO_PLC_EVENTOUT_VALID         (GPIO_PLC_EVENTOUT_PORT->idt & GPIO_PLC_EVENTOUT_PIN)

// PLC 12V电源控制(OFF代表关闭输出，ON代表输出12V)
#define GPIO_PLCPOWER_PORT              (GPIOD)
#define GPIO_PLCPOWER_PIN               (GPIO_PIN_7)
#define GPIO_PLCPOWER_OFF               ((GPIO_PLCPOWER_PORT->clr) = GPIO_PLCPOWER_PIN)
#define GPIO_PLCPOWER_ON                ((GPIO_PLCPOWER_PORT->scr) = GPIO_PLCPOWER_PIN)


// 模块工作状态
enum
{
    PLC_STAT_PWROFF = 0,                    // 关机状态
    PLC_STAT_PWRON,
};


// 模块操作状态
enum
{
    PLC_OPSTEP_SHUTOFF = 0,                 // 关机状态
    PLC_OPSTEP_WAITPWRON,                   // 关机后等待再次上电
    PLC_OPSTEP_RESET,                       // 复位阶段
    PLC_OPSTEP_INIT,                        // 复位后的初始化阶段
    PLC_OPSTEP_NORMAL,                      // 正常工作阶段(透传)
};

typedef union
{
    struct 
    {
        u8 NeedPwrOff : 1;                  //是否需要关机
        u8 NeedReset : 1;                   //是否需要对模块进行复位操作
    }BitsField;
}PLC_OptFlagType;

// 模块参数块
typedef struct 
{
    u16 bps;
    u16 keepLive;                           // 单位秒 为0时代表不开启保活功能
}PLC_PARA_TYPE;


// 模块主控制块定义
typedef struct 
{
    u32 ticker;
    u32 EvtTM;                              // evt管腿拉为有效时的时标(系统秒)
    u32 ComlinkTM;                          // 通信活动时标(系统秒)
    u32 rstTM;                              // 模块复位时标(系统秒)
    u32 pwrOffTM;                           // 模块关机时标(系统秒)
    u32 nextPwrOnTM;                        // 等待下次开机的时标(系统秒)  =0说明不用再次上电开机
    PLC_OptFlagType OpFlag;
    u8 OpStep;                              // 模块工作步骤/状态
    u8 stat;
    u8 IsAllowPwrOn;                        // 电源功耗控制是否允许模块运行
    u8 rstCnt;
}PLC_CTRL_TYPE;

// 协议处理过程中,仅用于本模块的变量类型
typedef struct
{
    u32_byte DI;						// 本次通信帧DI
    u8 Ctl;								// 接收帧控制字
    u8 Error;							// 通信错误状态字
    u32_byte ClearDI;					// 专用于清零DI	// 注意:用完再作清零,上电复位清零
                                        // byte[0]--清零类型 byte[1]--DI1 byte[2]--DI2 byte[3]--DI3
    u8 Event_RecType;					// 事件记录类型
    u8 AdrType;							// 地址类: FRAME_IS_SELF_ADR(0)、FRAME_IS_BROADCAST_ADR(1)、FRAME_IS_SUPER_ADR(2)、FRAME_IS_FULL_ADR(3)
}Dl645_Public_Type;

typedef struct
{
    u8 Level;
    u8 PassWord[3];
}PassWord_Type;


// Dlt645 参数
typedef struct
{
    u8 CommunicateAddr[6];				// 645通信地址,共9位
    // ----------------------------------------------------------------------------------------
    // 状体字模式控制
    // BIT0|BIT1：
    //			 00 旧版645 上送当前存在的告警信息
    //			 01 新版本645只送新变位信息
    //			 10 新版本645上送当前存在的告警信息
    // BIT2:不上送事件发生次数(只送12个字节)
    u16 StatusMode;						// 主动上送参数
    // ----------------------------------------------------------------------------------------
    u16 ReportModeWord[4];				// 主动上报模式字
    u16 RefreshReport;					// 主动上报状态字自动复位延时时间

    // 645密码
    // 第一个字节为密码等级，后三个字节密码高字节在前
    PassWord_Type Dlt645_Pswd[Dl645_PASSWORD_Level];
    u8 eCommDotNum;						// 电能量通讯小数位数

    u8 SecurityEn;						// DLT645 安全性(密码)验证使能开关

    u8 reserve[43-1];					// 预留

    u16 Check;							// 校验
}DLT645_PARA_TYPE;

// 协议处理过程中,与通信端口相关的变量类型
typedef struct
{
    // 协议相关变量,请勿修改
    struct Dl645_Protocol_T
    {
        u8 fChangeBaud		:1;			// 更改波特率标志	// 注意:用完再作清零,上电复位清零

        u8 SpeedValue;					// 波特率特征字	// 注意:用完再作清零,上电复位清零
        u8 nBytes; 						// 接收到的数据长度或者需要发送的数据长度
        u8 Reserve;						// 保留
        u32_byte DI;					// 本次通信帧ID,读后续帧时可以用到
    }Protocol;
    // 数据块及后续帧涉及变量,无此功能可屏蔽
    struct Dl645_Block_T
    {
        u8 fSingleDI		:1;			// 单个ID项带后续帧标志
        u8 fBlockDI			:1;			// 数据块ID项带后续帧标志

        u8 FrameNo;						// 后续帧序号
        u8 SingleStartDI;				// 单个数据项起始ID(内部ID)
        u8 BlockStartDI;				// 数据块起始DL645-ID(外部ID)
    }Sequent;
    // 曲线模块所需变量,无负荷记录可屏蔽
    struct Dl645_Curve_T
    {
        u8 Blocks; 						// 曲线块数
        u8 Reserve;						// 保留
    }Curve;
}Dl645_Vari_Type;

// =====================================================================================
// Variable Declares
// =====================================================================================
extern PLC_CTRL_TYPE PLCCtrl;
extern PLC_PARA_TYPE PLCPara;
extern DL645Frame responseModbus;

extern Dl645_Public_Type Dl645_Public;
extern DLT645_PARA_TYPE g_Dlt645Para;

extern Dl645_Vari_Type Port_Vari[COMDRV_PORT_MAX];  //

// =====================================================================================
// Functions Declares
// =====================================================================================
static bool PLC_IsRunOK(void);
void PLC_Port_Init(u32 dwBaudrateNo);
void PLC_Init(void);
void PLC_Service(void);
void PLC_Reset(void);
UINT32 GetSystemRuntimeSeconds();

#endif
// =====================================================================================
// End
// =====================================================================================

